/**
 * This file is part of "Subversion Server Configuration (win32)".

    "Subversion Server Configuration (win32)" is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    "Subversion Server Configuration (win32)" is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with "Subversion Server Configuration (win32)".  If not, see <http://www.gnu.org/licenses/>.
 * 
 *
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;

namespace Subversion_Configuration_Win32
{
    public partial class FORM_Repo : Form
    {
        private SvnRepo repo;

        public SvnRepo NewRepository
        {
            get { return repo; }
        }

        private enum repo_type { READONLY, FULL };

        


        public FORM_Repo(String title)
        {
            InitializeComponent();
            this.Text = title;
        }       

        public FORM_Repo(TreeNodeCollection all_groups, TreeNodeCollection all_users)
        {
            InitializeComponent();
            TreeNode u_Root = new TreeNode("Ungrouped users");
            TreeNode all_Root = new TreeNode("All");

            foreach (TreeNode n in all_groups)
            {
                all_Root.Nodes.Add(cloneSubTree(n));
            }

            if ((all_users != null) && (all_users.Count > 0))
            {
                foreach (TreeNode n in all_users)
                {
                    u_Root.Nodes.Add(cloneSubTree(n));
                }

                all_Root.Nodes.Add(u_Root);
            }

            this.treeView_readonly_access_repo.Nodes.Add(all_Root);
            this.treeView_fullaccess_repo.Nodes.Add(cloneTree(all_Root));

            this.treeView_readonly_access_repo.ExpandAll();
            this.treeView_fullaccess_repo.ExpandAll();
        }

        public FORM_Repo(TreeNodeCollection all_groups, TreeNodeCollection all_users, String modifyRepoName, String repoToken )
        {
            InitializeComponent();
            TreeNode u_Root = new TreeNode("Ungrouped users");
            TreeNode all_Root = new TreeNode("All");

            foreach (TreeNode n in all_groups)
            {
                all_Root.Nodes.Add(cloneSubTree(n));
            }

            if ((all_users != null) && (all_users.Count > 0))
            {
                foreach (TreeNode n in all_users)
                {
                    u_Root.Nodes.Add(cloneSubTree(n));
                }

                all_Root.Nodes.Add(u_Root);
            }

            this.treeView_readonly_access_repo.Nodes.Add(all_Root);
            this.treeView_fullaccess_repo.Nodes.Add(cloneTree(all_Root));

            checkRepositoryUsersAndGroups(modifyRepoName,repoToken);

            this.textBox_repositoryName.Text = modifyRepoName;

            this.treeView_readonly_access_repo.ExpandAll();
            this.treeView_fullaccess_repo.ExpandAll();


        }

        private void checkRepositoryUsersAndGroups(string modifyRepoName, String repoToken)
        {
            if (repoToken != null)
            {
                Char[] par2 ={ '\r', '\n' };
                String[] lines = repoToken.Split(par2, StringSplitOptions.RemoveEmptyEntries);
                if (lines != null)
                {
                    foreach (String line in lines)
                    {
                        String[] bean = line.Split('=');
                        if ((bean != null) && (bean.Length == 2))
                        {
                            String access = bean[1]; //r or rw
                            String item = bean[0]; //*, @group or user

                            TreeView tree = null;


                            if (access.Equals("r"))
                                tree = this.treeView_readonly_access_repo;
                            else
                                tree = this.treeView_fullaccess_repo;

                            if (item.Equals("*"))
                            {
                                tree.SelectedNode = tree.Nodes[0];
                            }
                            else if (item[0].Equals('@'))
                            { //a group  
                                TreeNode found = Utility.getFirstNode(tree.Nodes[0], item.Substring(1));
                                if (found != null)
                                    tree.SelectedNode = found;
                                else throw new Exception("Node not found in tree");
                            }
                            else //user
                            {
                                TreeNode found = Utility.getFirstNode(tree.Nodes[0], item);
                                if (found != null)
                                    tree.SelectedNode = found;
                                else throw new Exception("Node not found in tree");
                            }

                            if (access.Equals("r"))
                                treeView_readonly_access_repo_Click(null, null);
                            else
                                treeView_fullaccess_repo_Click(null, null);
                        }
                    }
                }
            }
            
        }

        private TreeNode cloneTree(TreeNode root)
        {
            TreeNode newRoot = new TreeNode(root.Text);
            if (root.Nodes != null)
            {
                foreach (TreeNode n in root.Nodes)
                {
                    newRoot.Nodes.Add(cloneSubTree(n));
                }
            }
            return newRoot;            
        }

        private TreeNode cloneSubTree(TreeNode n)
        {
            return cloneSubTree(n.Nodes, n);
        }

        //
        private TreeNode cloneSubTree(TreeNodeCollection nodes, TreeNode root)
        {
            
            if (nodes != null)
            {
                TreeNode cloned=new TreeNode(root.Text);
                foreach (TreeNode node in nodes)
                {
                    TreeNode[] arrayTreeChildren = new TreeNode[node.Nodes.Count];
                    
                    for (int i = 0; i < node.Nodes.Count;i++ )
                    {
                        arrayTreeChildren[i] = cloneSubTree(node.Nodes,node.Parent);
                    }
                    cloned.Nodes.Add(new TreeNode(node.Text, arrayTreeChildren));
                }

                return cloned;
            }
            
            return null;

        }
  

        private void checkSameUser(TreeNode userNameNode, TreeNode root,bool isChecked)
        {    
            TreeNodeCollection tree= root.Nodes;

            foreach (TreeNode n in tree)
            {
                if (!n.Equals(userNameNode))
                {
                    if (n.Text.Equals(userNameNode.Text))
                    {
                        n.Checked = userNameNode.Checked;

                        if (!isChecked || allSiblingsChecked(n))
                            checkUncheckParents(n, isChecked); 
                        /*
                        if (n.ForeColor != Color.Gray)
                            n.ForeColor = Color.Gray;
                        else
                            n.ForeColor = Color.Black;
                         */
                        
                    }
                }

                checkSameUser(userNameNode, n, isChecked);
            }
        }
        

        private void checkUncheckChildren(TreeNodeCollection nodes, bool isChecked)
        {
            foreach (TreeNode n in nodes)
            {
                n.Checked = isChecked;                
                checkSameUser(n, n.TreeView.Nodes[0], isChecked);
                
                checkUncheckChildren(n.Nodes,isChecked);
            }
        }

        private void treeView_readonly_access_repo_Click(object sender, EventArgs e)
        {
            
            TreeNode checkedNode = this.treeView_readonly_access_repo.SelectedNode;
            if (checkedNode != null)
            {
                bool isChecked = !checkedNode.Checked;
                checkedNode.Checked = isChecked;
                if (checkedNode.Nodes.Count > 0)
                {
                    checkUncheckChildren(checkedNode.Nodes, isChecked);
                }         
                    
                if(!isChecked || allSiblingsChecked(checkedNode))
                    checkUncheckParents(checkedNode, isChecked);                

                TreeNode tree = treeView_readonly_access_repo.Nodes[0];
                checkSameUser(checkedNode, tree, isChecked);

                

            }

        }


        //check if all the siblings' nodes are checked
        private bool allSiblingsChecked(TreeNode checkedNode)
        {
            TreeNode parentNode = checkedNode.Parent;
            if (parentNode != null)
            {
                foreach (TreeNode sibling in parentNode.Nodes) //at least one node (the checkedNode) is present
                {
                    if (!sibling.Checked)
                        return false;
                }                 
            }
            return true;
          
                                 
        }

        private void checkUncheckParents(TreeNode checkedNode, bool isChecked)
        {
            TreeNode parent=checkedNode.Parent;
            while (parent != null)
            {
                parent.Checked = isChecked;
                if (!isChecked || allSiblingsChecked(parent))
                    parent = parent.Parent;
                else 
                    break;
            }
                
        }

        private void treeView_fullaccess_repo_Click(object sender, EventArgs e)
        {            
            TreeNode checkedNode = this.treeView_fullaccess_repo.SelectedNode;
            if (checkedNode != null)
            {
                bool isChecked = !checkedNode.Checked;
                checkedNode.Checked = isChecked;
                if (checkedNode.Nodes.Count > 0)
                {                    
                    checkUncheckChildren(checkedNode.Nodes, isChecked);
                }

                if (!isChecked || allSiblingsChecked(checkedNode))
                    checkUncheckParents(checkedNode, isChecked);               

                TreeNode tree = treeView_fullaccess_repo.Nodes[0];
                checkSameUser(checkedNode, tree, isChecked);
            }
        }

        private void treeView_fullaccess_repo_MouseDown(object sender, MouseEventArgs e)
        {
            this.treeView_fullaccess_repo.SelectedNode = this.treeView_fullaccess_repo.GetNodeAt(e.X, e.Y);


        }

        private void treeView_readonly_access_repo_MouseDown(object sender, MouseEventArgs e)
        {
            this.treeView_readonly_access_repo.SelectedNode = this.treeView_readonly_access_repo.GetNodeAt(e.X, e.Y);
        }

        private void treeView_readonly_access_repo_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Action != TreeViewAction.Unknown)
            {
                //TreeNode checkedNode = this.treeView_readonly_access_repo.SelectedNode;

            }

        }

        private void treeView_readonly_access_repo_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {

        }

        private void button_cancel_repo_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void button_save_repo_Click(object sender, EventArgs e)
        {
            String reponame = this.textBox_repositoryName.Text;


            if (checkRepositoryName(reponame) || this.Text.Equals("Modify Repository"))
            {
                this.repo = new SvnRepo(reponame);

                String[] c_groups = getCheckedGroups(repo_type.READONLY);
                String[] c_users = getCheckedUsers(repo_type.READONLY);

                this.repo.ReadOnly_Groups = c_groups;
                this.repo.ReadOnly_Users = c_users;

                c_users = getCheckedUsers(repo_type.FULL);
                c_groups = getCheckedGroups(repo_type.FULL);

                this.repo.FullAccess_Groups = c_groups;
                this.repo.FullAccess_Users = c_users;

                this.repo.sanitizeGroups();

                this.repo.sanitizeUsers();

                this.Close();
            }
            else MessageBox.Show("Please change the repository name (in use)", "Change Name");            

        }

        private string[] getCheckedUsers(repo_type t)
        {
            String[] retVal = null;
            TreeView tree = null;
            switch (t)
            {
                case repo_type.READONLY:
                    {
                        tree = this.treeView_readonly_access_repo;
                        break;
                    }
                case repo_type.FULL:
                    {
                        tree = this.treeView_fullaccess_repo;
                        break;
                    }
                default: throw new Exception("Invalid Repository Type");
            }

            if (tree != null)
            {
                if (tree.Nodes[0].Checked) //all checked
                {
                    retVal = new String[1];
                    retVal[0] = "all";
                }
                else
                {
                    int j = 0;
                    TreeNodeCollection subtree = tree.Nodes[0].Nodes;
                    int numnodes = subtree.Count;
                    List<String> tempList = new List<string>();
                    while (j < numnodes)
                    {
                        TreeNode tempGroup = subtree[j++];
                        if ((tempGroup.Nodes != null))
                        {
                            if (!areChildrenChecked(tempGroup) || (tempGroup.Text.Equals("Ungrouped users"))) //not all children checked
                            {
                                foreach (TreeNode child in tempGroup.Nodes)
                                {
                                    if ((child.Checked) && (!tempList.Contains(child.Text)))
                                        tempList.Add(child.Text);
                                }
                            }
                        }
                    }

                    if (tempList.Count > 0)
                    {
                        retVal = new String[tempList.Count];
                        int i = 0;
                        foreach (String u in tempList)
                        {
                            retVal[i++] = u;
                        }
                    }
                }
            }

            return retVal;
        }

        private string[] getCheckedGroups(repo_type t)
        {
            String[] retVal = null;
            TreeView tree = null;
            switch (t)
            {
                case repo_type.READONLY:
                    {
                        tree = this.treeView_readonly_access_repo;
                        break;
                    }
                case repo_type.FULL:
                    {
                        tree = this.treeView_fullaccess_repo;
                        break;
                    }
                default: throw new Exception("Invalid Repository Type");                    
            }

            if (tree != null)
            {
                if (tree.Nodes[0].Checked) //all checked
                {
                    retVal = new String[1];
                    retVal[0] = "all";
                }
                else
                {
                    int j = 0;
                    TreeNodeCollection subtree = tree.Nodes[0].Nodes;
                    int numnodes = subtree.Count;
                    List<String> tempList = new List<string>();
                    while (j < numnodes)
                    {
                        TreeNode tempGroup = subtree[j++];
                        if (!tempGroup.Text.Equals("Ungrouped users"))
                        {
                            if ((tempGroup.Nodes != null) && areChildrenChecked(tempGroup))
                            {
                                tempList.Add(tempGroup.Text);
                            }
                        }
                    }

                    if (tempList.Count > 0)
                    {
                        retVal = new String[tempList.Count];
                        int i = 0;
                        foreach (String g in tempList)
                        {
                            retVal[i++] = g;                            
                        }
                    }
                }
            }
            return retVal;
        }

        //return true if all children are checked
        private bool areChildrenChecked(TreeNode tempGroup)
        {
            if ((tempGroup != null) && (tempGroup.Nodes != null))
            {
                foreach (TreeNode child in tempGroup.Nodes)
                {
                    if (!child.Checked)
                        return false;
                }
                return true;
            }
            return false;
        }     

        //return true if the name is not used yet
        private bool checkRepositoryName(string reponame)
        {
            if ((reponame == null) || reponame.Equals(""))
                return false;            

            svn_files settings = new svn_files();

            String[] dirs=Directory.GetDirectories(settings.subversion_REPOS_PATH);
            foreach (String dir in dirs)
            {
                
                if (dir.Equals(Path.Combine(settings.subversion_REPOS_PATH,reponame)))
                    return false;
            }
            return true;
        }
        

        
    }
}